/*
 * DiagramConfigUtils.java
 * Copyright 2022 HuaWang Tech, all rights reserved.
 * HuaWang PROPRIETARY/CONFIDENTIAL, any form of usage is subject to approval.
 */

package com.huawang.mdesign.model.service.inter;

import cn.hutool.core.collection.CollUtil;
import com.google.common.collect.Sets;
import com.huawang.business.constants.*;
import com.huawang.business.dto.ModelPropertyInfo;
import com.huawang.business.dto.graph.CanCreateModel;
import com.huawang.business.dto.graph.ConnectInfo;
import com.huawang.business.dto.graph.ModelInfo;
import com.huawang.business.dto.project.Project;
import com.huawang.business.service.customization.ICustomizationService;
import com.huawang.business.service.model.base.ElementCheckService;
import com.huawang.business.service.model.base.ElementModelInfoService;
import com.huawang.business.service.model.core.InternalUmlService;
import com.huawang.business.service.model.core.ModelPropertyInfoService;
import com.huawang.business.service.model.core.project.ProjectManager;
import com.huawang.business.util.InstanceUtils;
import com.huawang.core.definition.mof.UMLDefinitionConstants;
import com.huawang.core.definition.mof.cmof.reflection.impl.BasicRElement;
import com.huawang.core.definition.mof.instances.MetaClassInstance;
import com.huawang.core.definition.mof.kernel.structureClassifiers.MClass;
import com.huawang.core.definition.query.IQueryDefinitionService;
import com.huawang.core.semantics.service.query.IJudgeService;
import com.huawang.mdesign.model.service.config.MessageProcessor;
import com.huawang.mdesign.model.service.config.UAFModelManagerConfig;
import com.huawang.mdesign.model.service.constant.UAFDefinitionConstants;
import com.huawang.mdesign.model.service.constant.UAFDiagramTagEnum;
import com.huawang.mdesign.model.service.constant.cancreatetags.CustomizationDiagramConstants;
import com.huawang.mdesign.model.service.constant.cancreatetags.CustomizationElementConstants;
import com.huawang.mdesign.model.service.dto.BatchConnectInfo;
import com.huawang.mdesign.model.service.dto.RelationshipEndsInfoDTO;
import com.huawang.mdesign.model.service.dto.ValidateRelationCreationDto;
import com.huawang.mdesign.model.service.util.LogUtil;
import com.huawang.mdesign.model.service.vo.CanCreateDiagramAndElementVo;
import com.huawang.mdesign.model.service.vo.CanCreateDiagramVo;
import com.huawang.mdesign.model.service.vo.ElementVo;
import com.huawang.mdesign.model.service.facade.validate.common.UAFValidateUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
@Primary
@Slf4j
public class DiagramConfigService {

    @Autowired
    private InternalUmlService internalUmlService;

    @Autowired
    private UAFModelManagerConfig managerConfig;

    @Autowired
    private MessageProcessor messageProcessor;

    @Autowired
    private ElementModelInfoService elementModelInfoService;

    @Autowired
    private ProjectManager projectManager;

    @Autowired
    private UAFValidateUtils uafValidateUtils;

    @Autowired
    private ICustomizationService customizationService;

    @Autowired
    private ModelPropertyInfoService modelPropertyInfoService;

    @Autowired
    private ElementCheckService elementCheckService;

    @Autowired
    private IQueryDefinitionService queryDefinitionService;

    @Autowired
    private IJudgeService judgeService;

    @NotNull
    public List<ElementVo> canCreateRelation(final String projectId, final String instanceId, final String type) {
        LogUtil.printOnlineLogs("projectId: {}, instanceId: {}, type: {}", projectId, instanceId, type);
        if (Objects.equals(RelationExtendTypeEnum.OUTGOING.value(), type)) {
            return canCreateRelation(projectId, instanceId, RelationExtendTypeEnum.OUTGOING);
        } else {
            return canCreateRelation(projectId, instanceId, RelationExtendTypeEnum.INCOMING);
        }

    }

    @NotNull
    public List<ElementVo> batchConnectInfo(BatchConnectInfo dto) {
        RelationExtendTypeEnum typeEnum = Objects.equals(RelationExtendTypeEnum.OUTGOING.value(), dto.getType()) ? RelationExtendTypeEnum.OUTGOING : RelationExtendTypeEnum.INCOMING;

        Map<String,ElementVo> elementVoMap = new HashMap<>();
        Set<Set<String>> cnNameList = Sets.newHashSet();
        for (String instanceId : dto.getInstanceIds()) {
            List<ElementVo> list = canCreateRelation(dto.getProjectId(), instanceId, typeEnum);
            for (ElementVo elementVo : list) {
                elementVoMap.put(elementVo.getCnName(), elementVo);
            }
            cnNameList.add(list.stream().map(ElementVo::getCnName).collect(Collectors.toSet()));
        }

        Set<String> cnName = CollUtil.get(cnNameList, 0);

        for (int i = 1; i < cnNameList.size(); i++) {
            cnName = CollUtil.intersectionDistinct(cnName, CollUtil.get(cnNameList,i));
        }

        return cnName.stream().map(elementVoMap::get).collect(Collectors.toList());
    }

    @NotNull
    public List<ElementVo> canCreateRelation(final String projectId, final String instanceId, final RelationExtendTypeEnum type) {
        LogUtil.printOnlineLogs("projectId: {}, instanceId: {}, type: {}", projectId, instanceId, type.value());
        final MetaClassInstance instance = internalUmlService.fetchInstance(instanceId, projectId);
        if (Objects.isNull(instance)) {
            return Collections.emptyList();
        }
        String key = getMenuKey(projectId, instanceId, managerConfig.getConnectInfoConfig());
        final List<ElementVo> result = new ArrayList<>();
        final ConnectInfo connectInfos = this.managerConfig.getConnectInfoConfig().get(key);
        List<String> modelList;
        if (Objects.nonNull(connectInfos)) {
            modelList = Objects.equals(RelationExtendTypeEnum.OUTGOING, type) ? connectInfos.getOutgoing() : connectInfos.getIncoming();
        } else {
            modelList = this.getValidRelationship(instance, projectId, type);
        }
        Optional.ofNullable(modelList).ifPresent(mList -> mList.forEach(it -> {
            final ElementVo elementVo = new ElementVo();
            if (managerConfig.getModelInfoConfig().containsKey(it)) {
                final ModelInfo info = managerConfig.getModelInfoConfig().get(it);
                if (StringUtils.isEmpty(info.getMetaclass())) {
                    return;
                }
                elementVo.setMetaclass(info.getMetaclass());
                elementVo.setCnName(info.getCnName());
                elementVo.setEnName(info.getEnName());
                elementVo.setIcon(info.getIcon());
                elementVo.setAppliedStereotypes(info.getAppliedStereotypes());
                elementVo.setValue(info.getValue());
                result.add(elementVo);
            }
        }));

        List<ElementVo> otherRelations = getCustomizationMetaRelation(projectId, instance, modelList, type);
        result.addAll(otherRelations);
        return result;
    }

    /**
     * desciption:根据多个关系类型查询两端可选的元素类型
     *
     * @param relationshipEndsInfoDTO
     * @return java.util.List<java.lang.String>
     * @author lx
     * @date 2023/10/11
     */
    @NotNull
    public List<String> relationshipEndsInfo(RelationshipEndsInfoDTO relationshipEndsInfoDTO) {
        LogUtil.printSerializeOnlineLogs("relationshipEndsInfoDTO: ", relationshipEndsInfoDTO);
        List<String> types = relationshipEndsInfoDTO.getTypes();
        final String projectId = relationshipEndsInfoDTO.getProjectId();
        return types.stream().flatMap(type -> {
            List<String> availableTypes = internalUmlService.getRelationSourceAvailableTypes(projectId,
                    type).stream().map(BasicRElement::getId).collect(Collectors.toList());
            availableTypes.addAll(availableTypes.stream().flatMap(it ->
                    internalUmlService.getMetaClassApplicableStereotype(projectId, it).stream()).collect(Collectors.toList()));
            return availableTypes.stream();
        }).distinct().collect(Collectors.toList());
    }

    /**
     * 获得可用的UML和配置的关系
     *
     * @param instance
     * @param projectId
     * @param type
     * @return
     */
    private List<String> getValidRelationship(final MetaClassInstance instance,
                                              final String projectId,
                                              final RelationExtendTypeEnum type) {
        List<String> reList = new ArrayList<>();
        ValidateRelationCreationDto validateDto = new ValidateRelationCreationDto();
        validateDto.setProjectId(projectId);
        if (Objects.equals(RelationExtendTypeEnum.OUTGOING, type)) {
            validateDto.setSource(instance.getId());
        } else {
            validateDto.setTarget(instance.getId());
        }
        //UML
        for (String umlRelationship : UAFDefinitionConstants.UML_RELATIONSHIP) {
            validateDto.setMetaclass(umlRelationship);
            if (uafValidateUtils.validateUmlRelationCreation(validateDto)) {
                reList.add(umlRelationship);
            }
        }
        //UAF配置
        Set<String> instanceTypes = internalUmlService.getGivenTypes(projectId, instance);
        this.managerConfig.getRelationshipConfig().forEach((key, value) -> {
            //如果模型的构造线与配置有交集，则说明这个关系是可行的
            List<String> types = new ArrayList<>(instanceTypes);
            if (Objects.equals(RelationExtendTypeEnum.OUTGOING, type)) {
                types.retainAll(value.getTypesForSource());
            } else {
                types.retainAll(value.getTypesForTarget());
            }
            if(!types.isEmpty()) {
                reList.add(key);
            }
        });
        return reList;
    }

    @NotNull
    public List<String> findSpecifics(final String projectId, final List<String> keys) {
        LogUtil.printOnlineLogs("project: {}, keys: {}", projectId, keys);
        return internalUmlService.findSpecificIdsCascade(projectId, keys, true);
    }

    @NotNull
    public List<ElementVo> getAllSpecificClass(final String projectId, final List<String> keys) {
        LogUtil.printOnlineLogs("project: {}, keys: {}", projectId, keys);
        List<MetaClassInstance> specificsList = internalUmlService.getSpecificsCascade(projectId, keys)
                .stream().distinct().collect(Collectors.toList());
        return specificsList.stream().map(it -> {
            Boolean isAbstract = elementCheckService.getIsAbstract(projectId, it);
            //只能显示非抽象的和非关系
            if (Boolean.FALSE.equals(isAbstract)) {
                String newKey = elementModelInfoService.getModelInfoKeyByStereotypeId(it.getId());
                final ElementVo elementVo = new ElementVo();
                ModelInfo info;
                if (managerConfig.getModelInfoConfig().containsKey(newKey)) {
                    info = managerConfig.getModelInfoConfig().get(newKey);
                } else {
                    info = internalUmlService.getCustomizationModelInfo(projectId, it, null);
                }

                if (info != null) {
                    elementVo.setMetaclass(info.getMetaclass());
                    elementVo.setCnName(info.getCnName());
                    elementVo.setEnName(info.getEnName());
                    elementVo.setIcon(info.getIcon());
                    elementVo.setAppliedStereotypes(info.getAppliedStereotypes());
                    return elementVo;
                }
            }
            return null;
        }).filter(Objects::nonNull).collect(Collectors.toList());
    }

    @NotNull
    public List<ElementVo> canCreateElement(final String projectId, final String instanceId) {
        LogUtil.printOnlineLogs("projectId：{}, instanceId：{}", projectId, instanceId);
        final Map<String, CanCreateModel> map = managerConfig.getCreateModelInfoConfig();
        String key = getMenuKey(projectId, instanceId, managerConfig.getCreateModelInfoConfig());
        List<ElementVo> result = new ArrayList<>();
        final List<String> canCreateTypes = getCanCreateTypes(map, key, result, ElementTagEnum.GENERAL_ELEMENT.name(),
                CustomizationElementConstants.FINAL_TAGS);

        result.addAll(getCustomizationMetaClass(projectId, instanceId, canCreateTypes, false));
        //过滤掉不符合规范的模型
        String projectSpecification = projectManager.getProject(projectId) != null?projectManager.getProject(projectId).getProjectSpecification():null;
        result = result.stream().filter(vo -> CollectionUtils.isEmpty(vo.getSpecifications()) ||
                (CollectionUtils.isNotEmpty(vo.getSpecifications()) &&
                        vo.getSpecifications().contains(projectSpecification))).collect(Collectors.toList());

        result.forEach(vo -> {
            List<String> appliedStereotypes = vo.getAppliedStereotypes();
            if(CollectionUtils.isNotEmpty(appliedStereotypes)) {
                String appliedStereotype = appliedStereotypes.get(0);
                if(appliedStereotype.startsWith("UAF::")) {
                    String appliedStereotypeSubStr = appliedStereotype.substring(5);
                    vo.setTag("ARCH_" + appliedStereotypeSubStr.substring(0, appliedStereotypeSubStr.indexOf("::")).replace(" ", "").toUpperCase());
                }
            }
        });
        return result;
    }

    @NotNull
    public List<CanCreateDiagramVo> canCreateDiagram(final String projectId, final String instanceId) {
        LogUtil.printOnlineLogs("projectId：{}, instanceId：{}", projectId, instanceId);
        final Map<String, CanCreateModel> map = managerConfig.getCreateDiagramInfoConfig();
        String key = getMenuKey(projectId, instanceId, map);
        if (!map.containsKey(key)) {
            MetaClassInstance instance = internalUmlService.fetchInstance(instanceId, projectId);
            List<MetaClassInstance> appliedStereotypes = internalUmlService.queryListInstanceProperty(projectId,
                    instance, UMLDefinitionConstants.Element_appliedStereotype);
            if(!appliedStereotypes.isEmpty()) {
                String baseStereotype = internalUmlService.queryBaseStereotype(projectId,
                        appliedStereotypes.get(0));
                if (null != baseStereotype) {
                    key = elementModelInfoService.getModelInfoKeyByStereotypeId(baseStereotype);
                } else {
                    key = elementModelInfoService.getModelInfoKeyByStereotypeId(appliedStereotypes.get(0).getId());
                }
            }
        }
        List<ElementVo> result = new ArrayList<>();
        final List<String> canCreateTypes = getCanCreateTypes(map, key, result, DiagramTagEnum.OTHER.name(),
                CustomizationDiagramConstants.FINAL_TAGS);

        result.addAll(getCustomizationMetaClass(projectId, instanceId, canCreateTypes, true));
        final List<CanCreateDiagramVo> canCreateDiagramVos = new ArrayList<>();
        final String projectSpecification = projectManager.getValidProject(projectId).getProjectSpecification();
        if (projectSpecification != null) {
            result = result.stream().filter(vo -> CollectionUtils.isEmpty(vo.getSpecifications()) ||
                            (CollectionUtils.isNotEmpty(vo.getSpecifications()) &&
                                    vo.getSpecifications().contains(projectSpecification)))
                    .collect(Collectors.toList());
        }
        result.stream().collect(Collectors.groupingBy(ElementVo::getTag)).forEach((k, v) -> {
            CanCreateDiagramVo vo = makeCanCreateDiagramVo(projectSpecification, k, v);
            canCreateDiagramVos.add(vo);
        });

        sortDiagramVos(canCreateDiagramVos, UAFDefinitionConstants.ORDER_OF_DIAGRAM_TAG);
        return canCreateDiagramVos;
    }

    @NotNull
    public CanCreateDiagramVo makeCanCreateDiagramVo(@Nullable final String projectSpecification,
                                                     @NotNull final String tag,
                                                     @NotNull final List<ElementVo> v) {
        CanCreateDiagramVo vo = new CanCreateDiagramVo();
        if (UAFDiagramTagEnum.OP.value().equals(tag) && managerConfig.isMilitary()) {
            vo.setLabel(UAFDefinitionConstants.UAF_OPERATIONAL_LABEL_MIL_NAME);
        } else {
            if (projectSpecification == null) {
                vo.setLabel(messageProcessor.getMessage(tag, null, tag));
            } else {
                vo.setLabel(messageProcessor.getMessage(tag, new String[]{projectSpecification}, tag));
            }
        }
        vo.setTag(tag);
        vo.setChildren(v);
        return vo;
    }

    public CanCreateDiagramAndElementVo diagramOrElementCreate(String projectId, String metaClass, List<String> stereotypes) {
        LogUtil.printOnlineLogs("diagramOrElementCreate projectId：{}, metaClass：{}，stereotypes:{}", projectId, metaClass, stereotypes);

        Map<String, CanCreateModel> map = managerConfig.getCreateDiagramInfoConfig();

        Map<String, CanCreateModel> modelInfoMap = managerConfig.getCreateModelInfoConfig();

        List<String> typeUris = new ArrayList<>();
        typeUris.add(metaClass);
        if (CollectionUtils.isNotEmpty(stereotypes)) {
            typeUris.addAll(stereotypes);
        }
        Set<String> givenTypes = typeUris.stream().flatMap(it -> this.queryDefinitionService.getGeneralsCascade(projectId, it, true).stream()).collect(Collectors.toSet());
        if (givenTypes.isEmpty()) {
            return null;
        }

        String key = getMenuKeyByMetaClassAndStereotypes(projectId, metaClass, stereotypes, givenTypes);

        //查询可创建的图
        List<ElementVo> diagramResult = new ArrayList<>();
        List<String> canCreateTypes = getCanCreateTypes(map, key, diagramResult, DiagramTagEnum.OTHER.name(),
                CustomizationDiagramConstants.FINAL_TAGS);

        List<MetaClassInstance> diagramOthers = customizationService.queryCanCreatedDiagramByCustomization(projectId, givenTypes);
        fillElementVos(projectId, diagramOthers, canCreateTypes, diagramResult);

        //查询可创建的元素
        final List<ElementVo> elementResult = new ArrayList<>();
        final List<String> elementCanCreateTypes = getCanCreateTypes(modelInfoMap, key, elementResult, ElementTagEnum.GENERAL_ELEMENT.name(),
                CustomizationElementConstants.FINAL_TAGS);
        List<MetaClassInstance> elementOthers = customizationService.queryCanCreatedElementByCustomization(projectId, givenTypes);
        fillElementVos(projectId, elementOthers, elementCanCreateTypes, elementResult);

        CanCreateDiagramAndElementVo canCreateDiagramAndElementVo = new CanCreateDiagramAndElementVo();
        canCreateDiagramAndElementVo.setDiagramElementVos(diagramResult);
        canCreateDiagramAndElementVo.setElementVos(elementResult);

        return canCreateDiagramAndElementVo;
    }

    private void fillElementVos(String projectId, List<MetaClassInstance> others, List<String> canCreateTypes, List<ElementVo> result) {
        others.forEach(other -> {
            ModelInfo info = getModelInfo(projectId, other, canCreateTypes);
            if (info != null) {
                final ElementVo elementVo = new ElementVo();
                fillElementVoByInfo(true, other, elementVo, info);
                result.add(elementVo);
            }
        });
    }


    private String getMenuKeyByMetaClassAndStereotypes(String projectId, String metaClass, List<String> stereotypes, Set<String> givenTypes) {
        MClass mClass = queryDefinitionService.queryMClass(projectId, metaClass);
        List<MClass> stereotypeMClasses = Collections.emptyList();
        if (CollectionUtils.isNotEmpty(stereotypes)) {
            stereotypeMClasses = queryDefinitionService.queryMClasses(projectId, stereotypes);
        }

        if (isGivenType(projectId, mClass, stereotypeMClasses, UMLDefinitionConstants.Enumeration)) {
            return UMLDefinitionConstants.Enumeration;
        }

        if (Objects.equals(mClass.getId(), UMLDefinitionConstants.Stereotype)) {
            return UMLDefinitionConstants.Stereotype;
        }

        if (givenTypes.contains(UMLDefinitionConstants.Model)) {
            return UMLDefinitionConstants.Model;
        }

        if (givenTypes.contains(UMLDefinitionConstants.State)) {
            return UMLDefinitionConstants.State;
        }

        if (givenTypes.contains(UMLDefinitionConstants.Extension)) {
            return UMLDefinitionConstants.Extension;
        }

        if (givenTypes.contains(UMLDefinitionConstants.AcceptEventAction)) {
            return UMLDefinitionConstants.AcceptEventAction;
        }

        if (givenTypes.contains(UMLDefinitionConstants.ConnectorEnd)) {
            return UMLDefinitionConstants.ConnectorEnd;
        }

        if (givenTypes.contains(UMLDefinitionConstants.Message)) {
            return UMLDefinitionConstants.Message;
        }
        String key = mClass.getId();
        if (CollectionUtils.isNotEmpty(stereotypes)) {
            List<MetaClassInstance> metaClassInstanceList = internalUmlService.fetchInstanceByIds(stereotypes, projectId);
            if (CollectionUtils.isNotEmpty(metaClassInstanceList)) {
                if (givenTypes.contains(UMLDefinitionConstants.SysML_TestCase)) {
                    if (isGivenType(projectId, mClass, stereotypeMClasses, UMLDefinitionConstants.Activity)) {
                        key = UMLDefinitionConstants.Customization_TestCaseActivity;
                    } else if (isGivenType(projectId, mClass, stereotypeMClasses, UMLDefinitionConstants.StateMachine)) {
                        key = UMLDefinitionConstants.Customization_TestCaseStateMachine;
                    } else if (isGivenType(projectId, mClass, stereotypeMClasses, UMLDefinitionConstants.Interaction)) {
                        key = UMLDefinitionConstants.Customization_TestCaseInteraction;
                    } else {
                        key = UMLDefinitionConstants.SysML_TestCase;
                    }
                } else {
                    //存在多个应用构造型，第一个可能是自定义构造型，导致模型的icon错误
                    key = InstanceUtils.fetchHideMetaTypeStereoType(key, metaClassInstanceList);
                }
            }
        }

        return elementModelInfoService.getModelInfoKeyByStereotypeId(key);
    }

    private boolean isGivenType(String projectId, MClass mClass, List<MClass> stereotypes, String givenType) {
        return judgeService.isGivenType(projectId, mClass, givenType) ||
                stereotypes.stream().filter(Objects::nonNull).anyMatch(e -> judgeService.isGivenType(projectId, e, givenType));
    }

    @NotNull
    private List<String> getCanCreateTypes(Map<String, CanCreateModel> map, String key, List<ElementVo> result,
                                           String newTag, Map<String, String> constantTagMap) {
        LogUtil.printOnlineLogs("key: {}, newTag: {}", key, newTag);
        final List<String> canCreateTypes;
        if (map.containsKey(key)) {
            canCreateTypes = map.get(key).getCanCreateTypes();
            canCreateTypes.forEach(it -> {
                final ElementVo elementVo = new ElementVo();
                if (managerConfig.getModelInfoConfig().containsKey(it)) {
                    final ModelInfo info = managerConfig.getModelInfoConfig().get(it);
                    elementVo.setMetaclass(info.getMetaclass());
                    elementVo.setCnName(info.getCnName());
                    elementVo.setEnName(info.getEnName());
                    elementVo.setIcon(info.getIcon());
                    elementVo.setAppliedStereotypes(info.getAppliedStereotypes());
                    elementVo.setSpecifications(info.getSpecifications());
                    elementVo.setValue(info.getValue());
                    String tag = constantTagMap.get(it);
                    if (tag == null) {
                        tag = newTag;
                    }

                    elementVo.setTag(tag);
                    result.add(elementVo);
                }
            });
        } else {
            canCreateTypes = new ArrayList<>();
        }
        return canCreateTypes;
    }

    private static void sortDiagramVos(List<CanCreateDiagramVo> canCreateDiagramVos, List<String> orderOfDiagramTag) {
        LogUtil.printSerializeOnlineLogs("canCreateDiagramVo: {}, orderOfDiagramTag: {}", canCreateDiagramVos,
                orderOfDiagramTag);
        canCreateDiagramVos.sort(((o1, o2) -> {
            if (orderOfDiagramTag.contains(o1.getTag()) && orderOfDiagramTag.contains(o2.getTag())) {
                return Integer.compare(orderOfDiagramTag.indexOf(o1.getTag()), orderOfDiagramTag.indexOf(o2.getTag()));
            } else if (orderOfDiagramTag.contains(o1.getTag()) && !orderOfDiagramTag.contains(o2.getTag())) {
                return -1;
            } else if (!orderOfDiagramTag.contains(o1.getTag()) && orderOfDiagramTag.contains(o2.getTag())) {
                return 1;
            } else {
                return 0;
            }
        }));
    }

    /**
     * 获取自定义的可创建模型
     *
     * @param instanceId
     * @param projectId
     * @param canCreateTypes
     * @return java.util.List<com.huawang.mdesign.model.service.vo.ElementVo>
     * @author 01
     * @date 2022/05/26 17:44
     */
    @NotNull
    private List<ElementVo> getCustomizationMetaClass(String projectId,
                                                      String instanceId,
                                                      List<String> canCreateTypes,
                                                      boolean isDiagram) {
        LogUtil.printOnlineLogs("projectId: {}, instanceId: {}, canCreateTypes: {}", projectId, instanceId, canCreateTypes);
        MetaClassInstance instance = internalUmlService.fetchInstance(instanceId, projectId);
        if (instance == null) {
            return Collections.emptyList();
        }

        List<MetaClassInstance> others;
        Project project = projectManager.getProject(projectId);
        if (Objects.isNull(project)) {
            return Collections.emptyList();
        }
        Set<String> typeUris = internalUmlService.getGivenTypes(projectId, instance);
        if (isDiagram) {
            others = customizationService.queryCanCreatedDiagramByCustomization(projectId, typeUris);
            others = Optional.of(others)
                    .filter(CollectionUtils::isNotEmpty)
                    .map(k -> {
                        List<String> ownerPackageIds = UAFDefinitionConstants.DIAGRAM_OWNER_MAP.get(project.getProjectSpecification());
                        List<MetaClassInstance> tmp = new ArrayList<>();
                        k.forEach(diagram -> {
                            // 视图、表、矩阵或者追溯图只能加入到对应的规范中
                            if (ownerPackageIds != null && ownerPackageIds.contains(diagram.getOwnerId())) {
                                tmp.add(diagram);
                            } else {
                                // 排除掉其他体系规范
                                // 自定义出来的图形
                                // HW定义的图形
                                final Collection<List<String>> values = UAFDefinitionConstants.DIAGRAM_OWNER_MAP.values();
                                if (diagram.getId().matches("^\\d+.*") ||
                                        UAFDefinitionConstants.COMMON_DIAGRAM.contains(diagram.getId())) {
                                    tmp.add(diagram);
                                }

                            }
                        });
                        return tmp;
                    }).orElse(others);
        } else {
            others = customizationService.queryCanCreatedElementByCustomization(projectId, typeUris);
        }

        if (CollectionUtils.isEmpty(others)) {
            return Collections.emptyList();
        }

        List<ElementVo> result = new ArrayList<>();
        others.forEach(other -> {
            ModelInfo info = getModelInfo(projectId, other, canCreateTypes);
            if (info != null) {
                final ElementVo elementVo = new ElementVo();
                fillElementVoByInfo(isDiagram, other, elementVo, info);
                result.add(elementVo);
            }

        });

        return result;
    }

    @Nullable
    private ModelInfo getModelInfo(String projectId, MetaClassInstance metaClassInstance, List<String> canCreateTypes) {
        LogUtil.printOnlineLogs("projectId: {}, metaClassInstanceId: {}, canCreateTypes: {}", projectId,
                metaClassInstance.getId(), canCreateTypes.toString());
        final String modelInfoKey = elementModelInfoService.getModelInfoKeyByStereotypeId(metaClassInstance.getId());
        if (canCreateTypes.contains(modelInfoKey)) {
            return null;
        }

        ModelInfo info;
        if (managerConfig.getModelInfoConfig().containsKey(modelInfoKey)) {
            info = managerConfig.getModelInfoConfig().get(modelInfoKey);
        } else {
            info = internalUmlService.getCustomizationModelInfo(projectId, metaClassInstance, null);
        }
        return info;
    }

    private static void fillElementVoByInfo(boolean isDiagram, MetaClassInstance other, ElementVo elementVo, ModelInfo info) {
        LogUtil.printOnlineLogs("isDiagram: {}, other: {}, elementVo: {}, ModelInfo：{}", String.valueOf(isDiagram),
                other.getId(), elementVo, info);
        elementVo.setMetaclass(info.getMetaclass());
        elementVo.setCnName(info.getCnName());
        elementVo.setEnName(info.getEnName());
        elementVo.setIcon(info.getIcon());
        elementVo.setAppliedStereotypes(info.getAppliedStereotypes());
        if (CustomizationElementConstants.FINAL_TAGS.containsKey(other.getId())) {
            elementVo.setTag(CustomizationElementConstants.FINAL_TAGS.get(other.getId()));
        }

        if (isDiagram) {
            if (DiagramConstants.TAGS.containsKey(other.getId())) {
                elementVo.setTag(DiagramConstants.TAGS.get(other.getId()));
            } else {
                elementVo.setTag(DiagramTagEnum.OTHER.name());
            }
        } else {
            if (ElementConstants.TAGS.containsKey(other.getId())) {
                elementVo.setTag(ElementConstants.TAGS.get(other.getId()));
            }
        }
    }

    @NotNull
    public List<ElementVo> getCustomizationMetaRelation(String projectId, MetaClassInstance instance,
                                                        @NotNull List<String> canCreateTypes,
                                                        RelationExtendTypeEnum type) {
        LogUtil.printOnlineLogs("projectId: {}, instance: {}, canCreateTypes: {}, type: {}", projectId, instance.getId(),
                canCreateTypes, type.value());
        List<MetaClassInstance> others = customizationService.queryCanCreatedRelation(projectId, instance, type);
        if (CollectionUtils.isEmpty(others)) {
            return Collections.emptyList();
        }

        List<ElementVo> result = new ArrayList<>();
        others.forEach(other -> {
            ModelInfo info = getModelInfo(projectId, other, canCreateTypes);
            if (info != null) {
                final ElementVo elementVo = new ElementVo();
                elementVo.setMetaclass(info.getMetaclass());
                elementVo.setCnName(info.getCnName());
                elementVo.setEnName(info.getEnName());
                elementVo.setIcon(info.getIcon());
                elementVo.setAppliedStereotypes(info.getAppliedStereotypes());
                elementVo.setValue(info.getValue());
                result.add(elementVo);
            }

        });
        return result;
    }


    public boolean validateConnect(final String projectId, final String instanceId,
                                   final String type, final String relation) {
        LogUtil.printOnlineLogs("projectId: {}, instanceId: {}, type: {}, relation: {}", projectId, instanceId, type, relation);
        final ConnectInfo info = managerConfig.getConnectInfoConfig().get(relation);
        if (Objects.isNull(info)) {
            log.warn("no relation type with config with the relation is:" + relation);
            return false;
        }
        final List<String> models = StringUtils.equalsIgnoreCase(RelationExtendTypeEnum.INCOMING.value(), type) ?
                info.getIncoming() : info.getOutgoing();
        if (CollectionUtils.isEmpty(models)) {
            log.warn("the model connect is empty with relation is: " + relation + "and the type is: " + type);
            return false;
        }
        final MetaClassInstance instance = internalUmlService.fetchInstance(instanceId, projectId);
        if (Objects.isNull(instance)) {
            log.warn("no this instance with instanceId is :" + instanceId);
            return false;
        }

        if (internalUmlService.checkInstanceType(projectId, instance, UMLDefinitionConstants.Enumeration)) {
            return models.contains(UMLDefinitionConstants.Enumeration);
        }

        return models.contains(elementModelInfoService.querySysModelInfoKey(projectId, instance));
    }

    @Nullable
    public String getMenuKey(@NotNull final String projectId, @NotNull final String instanceId, Map<String, ?> map) {
        LogUtil.printOnlineLogs("projectId: {}, instanceId: {}", projectId, instanceId);
        final MetaClassInstance instance = internalUmlService.fetchInstance(instanceId, projectId);
        if (Objects.isNull(instance)) {
            log.warn("no this instance with instanceId is :" + instanceId);
            return null;
        }

        if (internalUmlService.checkInstanceType(projectId, instance, UMLDefinitionConstants.Enumeration)) {
            return UMLDefinitionConstants.Enumeration;
        }
        ModelPropertyInfo model = modelPropertyInfoService.createSimpleModelPropertyInfo(projectId, instance,
                Stream.of(UMLDefinitionConstants.Element_appliedStereotype).collect(Collectors.toList()));
        String key = elementModelInfoService.querySysModelInfoKey(projectId, model);
        if (map.containsKey(key)) {
            return key;
        }

        List<MetaClassInstance> appliedStereotypes = model.queryListInstanceValue(UMLDefinitionConstants.Element_appliedStereotype);
        if (CollectionUtils.isEmpty(appliedStereotypes)) {
            MClass mClass = instance.getClassifier();
            return mClass.getId();
        }
        String baseStereotype = internalUmlService.queryBaseStereotype(projectId, appliedStereotypes);
        if (baseStereotype != null) {
            return elementModelInfoService.getModelInfoKeyByStereotypeId(baseStereotype);
        } else {
            return elementModelInfoService.getModelInfoKeyByStereotypeId(appliedStereotypes.get(0).getId());
        }
    }
}
